#pragma once //防止头文件重复包含
//代码主要目的：实现单例模式下的线程池

#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include <string>
#include <unordered_map>
#include <queue>
using namespace std;

struct ThreadInfo //保存线程信息
{
    string _name;
    pthread_t _tid;
};

const int N = 6; //创建5个线程


template<class T>
class ThreadPool
{
private: //构造函数私有，不允许外界创建对象
    ThreadPool(int num = N)
    : _threads(N)
    {
        //初始化锁和条件变量
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }
    ~ThreadPool()
    {
        for(int i = 0; i < _threads.size(); ++i)
            pthread_join(_threads[i]._tid, nullptr);
        //两种方案：1、手动释放 2、RAII
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        _tp = nullptr;
    }
private:
    //防止拷贝
    const ThreadPool<T>& operator= (const ThreadPool<T>& obj) = delete; //赋值运算符重载
    ThreadPool(const ThreadPool<T>& obj) = delete; //拷贝构造
public:
    static ThreadPool<T>* GetInstance() //获取单例对象的接口
    {
        if(nullptr == _tp) //提高效率，避免每层加锁 
        {
            //两层判断，防止多线程并行创建多个线程池
            //进入创建 
            pthread_mutex_lock(&_lock);
            if(nullptr == _tp)
            {
                std::cout << "log: singleton create done first!" << std::endl;
                _tp = new ThreadPool<T>(); //创建对象
            }
            pthread_mutex_unlock(&_lock);

            return _tp;
        }
        return _tp;
    }
    static bool DelInstance() //向外提供的接口
    {
        if(nullptr != _tp) //_tp不为空可以delete
        {
            delete _tp;
            return true;
        }
        return false;
    }
private:
    //封装锁的接口等接口
    void Lock() //加锁
    {
        pthread_mutex_lock(&_mutex);
    }
    void Unlock() //解锁
    {
        pthread_mutex_unlock(&_mutex);
    }
    void Wait() //条件等待
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void Wake() //唤醒
    {
        pthread_cond_signal(&_cond);
    }
    bool Istaskempty() //判断任务队列是否为空
    {
        return _task.empty();
    }
    const string Getthreadname(const pthread_t& tid) //获取名字
    {
        //return _map[tid]; //效率低
        for(int i = 0; i < _threads.size(); ++i)
        {
            if(tid == _threads[i]._tid) return _threads[i]._name;
        }
        return "None";
    }
public:
    //void* Handler(void* args); //这样的接口是不行的，因为有隐藏的this指针
    static void* Handler(void* args)
    {
        //因为static函数不能使用类的成员变量，解决办法：
        //1、传参需要传入this指针 2、使用上面获取单例的接口
        //方法二：ThreadPool<T>* tp = GetInstance(); 
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        std::string name = tp->Getthreadname(pthread_self());
        while(true)
        {
            tp->Lock();
            while (tp->Istaskempty()) //防止伪唤醒
                tp->Wait();

            T t = tp->Pop();
            tp->Unlock();

            t(); //运算数据
        }
    }
    void Start() //创建线程的接口
    {
        for(int i = 0; i < _threads.size(); ++i)
        {
            pthread_create(&(_threads[i]._tid), nullptr, Handler, this);
            _threads[i]._name = "Thread-" + to_string(i + 1);
            //下面：主线程建立映射
            //_map[_threads[i]._tid] = _threads[i]._name; //方便后续查找对应的名字,效率低下
        }
    }
    void Push(T&& t)
    {
        Lock();
        _task.push(forward<T>(t));
        Wake();
        Unlock();
    }

    T Pop()
    {
        T t = _task.front();
        _task.pop();
        return t;
    }
private:
    vector<ThreadInfo> _threads; //保存的数组信息
    
    //unordered_map<pthread_t, string> _map; //保存tid和name的映射关系，方便查找，但是效率极低
    //在创建的过程中，效率很低
    queue<Task> _task; //任务队列

    pthread_mutex_t _mutex; //保证线程池中的线程是互斥的 
    pthread_cond_t _cond; //保证线程池中的线程是同步的

    //注意：类模板下的静态成员变量初始化的时机是在第一次进行显示实例化的时候！！！
    static ThreadPool<T>* _tp; //单例模式下的指针
    static pthread_mutex_t _lock; //保证单例模式下的创建线程池的互斥性
    //static GC _gc; //RAII管理单例对象，程序结束自动销毁 —> 这样是不可行的！！
};


template <class T>
ThreadPool<T>* ThreadPool<T>::_tp = nullptr;

template <class T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER; //初始化

//template <class T>
//typename ThreadPool<T>::GC ThreadPool<T>::_gc; //静态成员变量需要在全局进行初始化
//typename声明类型
